namespace Digitalmes.Application.Mes;

/// <summary>
/// 产品返工单服务接口
/// </summary>
[ApiExplorerSettings(GroupName = "mes")]
public sealed class ReworkDocService : IDynamicWebApiService
{
    private readonly SugarRepository<ReworkDoc> _reworkDocRepo;
    private readonly SugarRepository<ProdTransSn> _transSnRepo;
    private readonly SugarRepository<ProcRouting> _routingRepo;
    private readonly SugarRepository<ReworkTemplate> _reworkTemplateRepo;

    private readonly WoIncrementService _woIncrementService;

    public ReworkDocService(SugarRepository<ReworkDoc> reworkDocRepo, 
        SugarRepository<ProdTransSn> transSnRepo,
        SugarRepository<ProcRouting> routingRepo,
        SugarRepository<ReworkTemplate> reworkTemplateRepo,
        WoIncrementService woIncrementService)
    {
        _reworkDocRepo = reworkDocRepo;
        _transSnRepo = transSnRepo;
        _routingRepo = routingRepo;
        _reworkTemplateRepo = reworkTemplateRepo;
        _woIncrementService = woIncrementService;
    }

    /// <summary>
    /// 查询所有——分页
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public async Task<PageResult<ReworkDocDto>> GetPagesAsync(PageParam param)
    {
        var query = await _reworkDocRepo.AsQueryable()
            .OrderByDescending(m => m.Id)
            .ToPageAsync(param.Page, param.Limit);
        return query.Adapt<PageResult<ReworkDocDto>>();
    }

    /// <summary>
    /// 获取 NG 产品分页信息
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    [HttpGet]
    public async Task<PageResult<ProdTransSnDto>> GetNgByPagesAsync(ReworkDocPageParam param)
    {
        // 查找当前有工艺路线、过站状态为 NG 的且还未开返工单的产品
        var query = await _transSnRepo.AsQueryable()
            .Where(s => s.Status == SnStatusEnum.NG && s.Stage == SnStageEnum.Out && s.CurRoutingId > 0 && s.ReworkFlag == ReworkFlagEnum.None)
            .WhereIF(!string.IsNullOrEmpty(param.Sn), s => s.Sn == param.Sn)
            .ToPageAsync(param.Page, param.Limit);
        return query.Adapt<PageResult<ProdTransSnDto>>();
    }

    /// <summary>
    /// 获取作业工序信息
    /// </summary>
    [HttpGet]
    public async Task<List<dynamic>> GetProcListAsync(ReworkProcListParam param)
    {
        // 通过工艺路线，找到所有的工序
        // 根据 NG 的工艺分类，判断默认选择工序：
        //  装配站/返工站 => NG 的下一工序；
        //  检测站 => NG 的当前工序

        if (param.RoutingId == 0)
        {
            return new(0);
        }

        var routing = await _routingRepo.AsQueryable()
           .Includes(s => s.RoutingItems, t => t.Process)
           .FirstAsync(s => s.Id == param.RoutingId);

        var routingItem = routing?.RoutingItems.FirstOrDefault(s => s.ProcessId == param.ProcId);
        if (routingItem == null)
        {
            return new(0);
        }

        if (routingItem.Process.Classify is ProcClassifyEnum.Assembly or ProcClassifyEnum.Repair)
        {
            var nextRoutingItem = routing!.RoutingItems.OrderBy(s => s.Pos).FirstOrDefault(s => s.Pos > routingItem.Pos);
            if (nextRoutingItem != null)
            {
                routingItem = nextRoutingItem;
            }
        }

        bool disabled = false, selected = false;
        List<object> result = new(routing!.RoutingItems.Count);
        foreach (var item in routing.RoutingItems.OrderBy(s => s.Pos))
        {
            if (param.DisableDone)
            {
                disabled = item.Pos < routingItem.Pos;
            }

            if (param.DisableTodo)
            {
                disabled = item.Pos > routingItem.Pos;
            }

            selected = item.Pos == routingItem.Pos;

            result.Add(new
            {
                Id = item.ProcessId,
                ProcId = item.ProcessId,
                ProcCode = item.ProcCode,
                Pos = item.Pos,
                Disabled = disabled,
                Selected = selected,
            });
        }

        return result;
    }

    /// <summary>
    /// 根据主键查询
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id}")]
    public async Task<ReworkDocDto> GetAsync(long id)
    {
        var model = await _reworkDocRepo.GetByIdAsync(id);
        return model.Adapt<ReworkDocDto>();
    }

    /// <summary>
    /// 根据SN查询
    /// </summary>
    /// <param name="sn"></param>
    /// <returns></returns>
    [HttpGet]
    public async Task<ReworkDocDto?> GetBySnAsync(string sn)
    {
        // 先从查看 SN 状态，检测是否再等待返工。
        var transSn = await _transSnRepo.GetFirstAsync(s => s.Sn == sn && s.ReworkFlag == ReworkFlagEnum.Wait);
        if (transSn == null)
        {
            return null;
        }

        // 找到属于 离线返工且还未返工的返工单
        var model = await _reworkDocRepo
           .AsQueryable()
           .Includes(s => s.TemplateItems)
           .OrderByDescending(s => s.Id)
           .FirstAsync(s => s.Doc == transSn.ReworkDoc 
                && s.ReworkType == ReworkTypeEnum.Offline 
                && new[] { ReworkStatusEnum.Created, ReworkStatusEnum.Issued }.Contains(s.Status));
        return model.Adapt<ReworkDocDto>();
    }

    /// <summary>
    /// 添加
    /// </summary>
    /// <param name="model"></param>
    /// <returns></returns>
    [UnitOfWork]
    public async Task<bool> AddAsync(ReworkDocDto model)
    {
        var transSn = await _transSnRepo.GetFirstAsync(s => s.Sn == model.Sn);
        if (transSn == null)
        {
            return false;
        }

        // SN 必须处于 NG 且未开返工单状态
        if (transSn.Status != SnStatusEnum.NG && transSn.ReworkFlag == ReworkFlagEnum.None)
        {
            return false;
        }

        // 检测该 SN 是否有未完成的返工单
        if (await _reworkDocRepo.IsAnyAsync(s => s.Sn == model.Sn && s.Status != ReworkStatusEnum.Completed))
        {
            return false;
        }

        var doc = new ReworkDoc
        {
            Doc = model.Doc,
            Sn = model.Sn,
            NgLineId = transSn.CurLineId,
            NgLineCode = transSn.CurLineCode,
            NgOpId = transSn.CurOpId,
            NgOpCode = transSn.CurOpCode,
            NgProcId = transSn.CurProcId,
            NgProcCode = transSn.CurProcCode,
            NgRoutingId = transSn.CurRoutingId,
            NgRoutingCode = transSn.CurRoutingCode,
            NgSteps = transSn.NgSteps,
            ReworkType = model.ReworkType,
            NeedRelaunch = model.ReworkType == ReworkTypeEnum.Online || model.NeedRelaunch, // 在线返工，固定为 true
            RelaunchProcId = model.RelaunchProcId,
            RelaunchProcCode = model.RelaunchProcCode,
            RelaunchProcStep = model.RelaunchProcStep,
            TemplateId = model.TemplateId,
            TemplateName = model.TemplateName,
            Status = ReworkStatusEnum.Created,
            Result = ReworkResultEnum.OK, // 默认结果
            ReworkProcCodes = new(),
        };

        // 计算返工的工序
        var routing = await _routingRepo.AsQueryable()
          .Includes(s => s.RoutingItems)
          .FirstAsync(s => s.Id == transSn.CurRoutingId);

        var reloadItem = routing.RoutingItems.First(s => s.ProcessId == doc.RelaunchProcId);
        var ngItem = routing.RoutingItems.First(s => s.ProcessId == doc.NgProcId);
        if (reloadItem.Pos >= ngItem.Pos)
        {
            doc.ReworkProcCodes.Add(reloadItem.ProcCode);
        }
        else
        {
            var arr = routing.RoutingItems
                .OrderBy(s => s.Pos)
                .Where(s => s.Pos >= reloadItem.Pos && s.Pos <= ngItem.Pos)
                .Select(s => s.ProcCode);
            doc.ReworkProcCodes.AddRange(arr);
        }

        // 修改 SN 流转数据状态
        transSn.ReworkFlag = ReworkFlagEnum.Wait;
        transSn.ReworkDoc = doc.Doc;
        await _transSnRepo.UpdateAsync(transSn, s => new { s.ReworkFlag, s.ReworkDoc });

        // 最后，离线返工，根据工序对应的模板添加返工明细
        if (doc.ReworkType == ReworkTypeEnum.Offline && doc.TemplateId > 0)
        {
            var template = await _reworkTemplateRepo.AsQueryable()
                .Includes(s => s.Subjects)
                .FirstAsync(s => s.Id == doc.TemplateId);

            var lines = template.Subjects?.Select(s => new ReworkDocLine
            {
                BadCode = s.BadCode,
                BadContent = s.BadContent,
            }).ToList();

            // 若存在模板数据，则导航新增数据
            if (lines?.Any() == true)
            {
                doc.TemplateItems = lines;
                return await _reworkDocRepo.Context
                    .InsertNav(doc)
                    .Include(s => s.TemplateItems)
                    .ExecuteCommandAsync();
            }
        }

        return await _reworkDocRepo.InsertAsync(doc);
    }

    /// <summary>
    /// 修改
    /// </summary>
    /// <param name="model"></param>
    /// <returns></returns>
    public async Task<bool> ModifyAsync(ReworkDocDto model)
    {
        var doc = await _reworkDocRepo.GetByIdAsync(model.Id);
        if (doc == null || doc.Status is not ReworkStatusEnum.Created)
        {
            return false;
        }

        doc.ReworkType = model.ReworkType;
        doc.NeedRelaunch = model.ReworkType == ReworkTypeEnum.Online || model.NeedRelaunch; // 在线返工，固定为 true
       
        if (doc.RelaunchProcId != model.RelaunchProcId)
        {
            doc.RelaunchProcId = model.RelaunchProcId;
            doc.RelaunchProcCode = model.RelaunchProcCode;
            doc.RelaunchProcStep = model.RelaunchProcStep;
            doc.ReworkProcCodes = new();

            var routing = await _routingRepo.AsQueryable()
                .Includes(s => s.RoutingItems)
                .FirstAsync(s => s.Id == doc.NgRoutingId);

            var reloadItem = routing.RoutingItems.First(s => s.ProcessId == doc.RelaunchProcId);
            var ngItem = routing.RoutingItems.First(s => s.ProcessId == doc.NgProcId);
            if (reloadItem.Pos >= ngItem.Pos)
            {
                doc.ReworkProcCodes.Add(reloadItem.ProcCode);
            }
            else
            {
                var arr = routing.RoutingItems
                    .OrderBy(s => s.Pos)
                    .Where(s => s.Pos >= reloadItem.Pos && s.Pos <= ngItem.Pos)
                    .Select(s => s.ProcCode);
                doc.ReworkProcCodes.AddRange(arr);
            }
        }

        // 更新模板，不管是否有切换模板
        doc.TemplateId = model.TemplateId;
        doc.TemplateName = model.TemplateName;

        var template = await _reworkTemplateRepo.AsQueryable()
            .Includes(s => s.Subjects)
            .FirstAsync(s => s.Id == doc.TemplateId);

        var lines = template.Subjects?.Select(s => new ReworkDocLine
        {
            BadCode = s.BadCode,
            BadContent = s.BadContent,
        }).ToList();

        if (lines?.Any() == true)
        {
            doc.TemplateItems = lines;
            return await _reworkDocRepo.Context
                .UpdateNav(doc)
                .Include(s => s.TemplateItems)
                .ExecuteCommandAsync();
        }

        return await _reworkDocRepo.UpdateAsync(doc);
    }

    /// <summary>
    /// 返工完工
    /// </summary>
    /// <param name="model"></param>
    /// <returns></returns>
    [HttpPost]
    [UnitOfWork]
    public async Task<bool> CompleteAsync(ReworkDocDto model)
    {
        // 返工单必须为离线返工且还未完工。
        var doc = await _reworkDocRepo.GetByIdAsync(model.Id);
        if (doc == null 
            || doc.ReworkType is not ReworkTypeEnum.Offline 
            || doc.Status is not ReworkStatusEnum.Created or ReworkStatusEnum.Issued)
        {
            return false;
        }

        doc.Status = ReworkStatusEnum.Completed;
        doc.Operator = AppUtils.LoginUser;
        doc.CompletedTime = DateTime.Now;
        doc.Result = ReworkResultEnum.OK; // 返工结果固定为 OK
        doc.ReworkLineCode = model.ReworkLineCode;
        doc.ReworkOpCode = model.ReworkOpCode;
        doc.Remark = model.Remark;
        doc.TemplateItems = model.TemplateItems.Adapt<List<ReworkDocLine>>();

        // 修改SN信息
        var transSn = await _transSnRepo.GetFirstAsync(s => s.Sn == model.Sn);
        if (transSn != null)
        {
            // 更新状态和返工数量
            transSn.Status = SnStatusEnum.OK;
            transSn.ReworkCount += 1;

            if (!doc.NeedRelaunch)
            {
                // 离线返工，且不需要重新上线，标记 SN 已完工，并重置返工标识
                transSn.IsCompelted = true;
                transSn.OfflineTime = DateTime.Now;

                transSn.ReworkFlag = ReworkFlagEnum.Done;
            }
            else
            {
                // 需重新上线
                transSn.ReworkFlag = ReworkFlagEnum.DoneAndWaitToRelaunch;
            }

            await _transSnRepo.UpdateAsync(transSn, s => new
            {
                s.Status,
                s.IsCompelted,
                s.OfflineTime,
                s.ReworkFlag,
                s.ReworkCount,
            });

            // 离线返工，且存在工单，则更新工单
            if (!string.IsNullOrEmpty(transSn.Wo))
            {
                await _woIncrementService.OfflineReworkIncrementAsync(transSn.Wo, doc.NeedRelaunch);
            }
        }

        return await _reworkDocRepo.Context
            .UpdateNav(doc, new UpdateNavRootOptions
            {
                UpdateColumns = new[] {
                    nameof(ReworkDoc.Status),
                    nameof(ReworkDoc.Operator),
                    nameof(ReworkDoc.CompletedTime),
                    nameof(ReworkDoc.Result),
                    nameof(ReworkDoc.ReworkLineCode),
                    nameof(ReworkDoc.ReworkOpCode),
                    nameof(ReworkDoc.Remark),
                    nameof(ReworkDoc.UpdateTime),
                    nameof(ReworkDoc.UpdateUser),
                }
            })
            .Include(s => s.TemplateItems)
            .ExecuteCommandAsync();
    }

    /// <summary>
    /// 删除,支持批量
    /// </summary>
    /// <param name="ids"></param>
    /// <returns></returns>
    [HttpDelete]
    public async Task<bool> DeleteAsync([FromBody] List<long> ids) =>
        await _reworkDocRepo.DeleteAsync(m => ids.Contains(m.Id));
}
